﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Edu.Http.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using IdentityModel.Client;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using System.Reflection.PortableExecutable;
using System.Web;

namespace Edu.Http.Proxy
{
    public abstract class BaseHttpClient : ApplicationService, ITransientDependency
    {
        protected readonly HttpClient _client;
        protected readonly ILogger<BaseHttpClient> _logger;

        protected virtual string SetHttpClientBaseAddress()
        {
            return null;
        }

        #region 构造函数

        protected BaseHttpClient(HttpClient client, ILoggerFactory loggerFactory, string baseAddress)
        {
            _client = client;
            _logger = loggerFactory.CreateLogger<BaseHttpClient>();

            if (!baseAddress.ToLower().StartsWith("http"))
            {
                baseAddress = string.Empty;
            }

            if (!string.IsNullOrEmpty(baseAddress))
            {
                _client.BaseAddress = new Uri(baseAddress);
            }

            _client.DefaultRequestHeaders.Accept.Clear();
            _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            _client.Timeout = TimeSpan.FromSeconds(100); //设置请求超时时间为100秒
        }


        public void SetEndPoint(string baseAddress)
        {
            if (!string.IsNullOrEmpty(baseAddress))
            {
                _client.BaseAddress = new Uri(baseAddress);
            }
        }

        #endregion

        #region HttpGet请求

        /// <summary>
        /// Get请求不带参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> GetAsync(string url, Dictionary<string, string> headers = null)
        {
            return SendJsonAsync(HttpMethod.Get, url, jsonStr: null, contentType: null, headers);
        }

        public async Task<Stream> GetStreamAsync(string url)
        {
            try
            {
                var rsp = await _client.GetAsync(url);
                rsp.EnsureSuccessStatusCode();
                return await rsp.Content.ReadAsStreamAsync();
            }
            catch (Exception e)
            {
                throw;
            }
        }

        /// <summary>
        /// Get请求，携带对象请求参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="data"></param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> GetAsync(string url, object data, Dictionary<string, string> headers = null)
        {
            if (data != null)
            {
                url = url + '?' + data.ToQueryString();
            }
            return GetAsync(url, headers);
        }

        /// <summary>
        /// Get请求，携带Json请求参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="jsonStr"></param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> GetJsonAsync(string url, string jsonStr, Dictionary<string, string> headers = null)
        {
            if (!string.IsNullOrEmpty(jsonStr))
            {
                url = url + '?' + jsonStr;
            }
            return GetAsync(url, headers);
        }

        /// <summary>
        /// Get请求不带参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 泛型</returns>
        public async Task<T> GetAsync<T>(string url, Dictionary<string, string> headers = null)
        {
            var response = await GetAsync(url, headers);
            return await ConvertResponseResult<T>(response);
        }

        /// <summary>
        /// GET请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="data">请求对象</param>
        /// <param name="headers">请求headers</param>
        /// <returns></returns>
        public async Task<T> GetAsync<T>(string url, object data, Dictionary<string, string> headers = null)
        {
            if (data != null)
            {
                url = url + '?' + data.ToQueryString();
            }
            var response = await GetAsync(url, headers);
            return await ConvertResponseResult<T>(response);
        }
        #endregion

        #region HttpPost请求

        /// <summary>
        /// Post请求
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="jsonStr">请求Json字符串</param>
        /// <param name="contentType">请求contentType</param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 泛型</returns>
        public async Task<T> PostJsonAsync<T>(string url, string jsonStr, string contentType = "", Dictionary<string, string> headers = null)
        {
            var response = await PostJsonAsync(url, jsonStr, contentType, headers);
            return await ConvertResponseResult<T>(response);
        }


        /// <summary>
        /// Post请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="data">请求对象</param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 泛型</returns>
        protected async Task<T> PostAsync<T>(string url, object data, Dictionary<string, string> headers = null)
        {
            var response = await PostAsync(url, data, headers);
            return await ConvertResponseResult<T>(response);
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 泛型</returns>
        protected async Task<T> PostAsync<T>(string url, Dictionary<string, string> headers = null)
        {
            var response = await PostAsync(url, null, headers);
            return await ConvertResponseResult<T>(response);

        }
        /// <summary>
        /// 上传导出的文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="fileName"></param>
        public async Task<T> PostAsync<T>(string url, string fileName)
        {
            HttpResponseMessage response = null;
            try
            {
                using var form = new MultipartFormDataContent();
                using var fileStream = new FileStream(fileName, FileMode.Open);
                var content = new StreamContent(fileStream);
                content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
                form.Add(content, "file", Path.GetFileName(fileName));

                response = await _client.PostAsync(url, form);
                return await ConvertResponseResult<T>(response);
            }
            catch (Exception ex)
            {
                throw new HttpProxyException($"SendObjAsync request failed:{ex.Message}{Environment.NewLine}{ex.StackTrace}{url}{response?.StatusCode.ToString()}");
            }
        }

        /// <summary>
        /// 上传导出的文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="content"></param>
        /// <param name="fileName"></param>
        public async Task<T> PostAsync<T>(string url, ByteArrayContent content, string fileName)
        {
            HttpResponseMessage response = null;
            try
            {
                using var form = new MultipartFormDataContent();
                content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
                content.Headers.ContentDisposition = GetContentDisposition(fileName);
                form.Add(content);
                response = await _client.PostAsync(url, form);

                return await ConvertResponseResult<T>(response);
            }
            catch (Exception ex)
            {
                throw new HttpProxyException(
                    $"SendObjAsync request failed:{ex.Message}{Environment.NewLine}{ex.StackTrace}{url}{response?.StatusCode.ToString()}");
            }
        }

        #endregion

        #region HttpPut请求

        /// <summary>
        /// Put请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="data">请求对象</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> PutAsync(string url, object data = null)
        {
            return SendObjectAsync(HttpMethod.Put, url, data);
        }

        public async Task<T> PutAsync<T>(string url, object data = null)
        {
            var response = await PutAsync(url, data);
            return await ConvertResponseResult<T>(response);
        }

        /// <summary>
        /// Put请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="jsonStr">请求json字符串</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> PutAsync(string url, string jsonStr)
        {
            return SendJsonAsync(HttpMethod.Put, url, jsonStr);
        }

        #endregion

        #region HttpPatch请求

        /// <summary>
        /// Patch请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="data">数据对象</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> PatchAsync(string url, object data = null)
        {
            return SendObjectAsync(HttpMethod.Patch, url, data);
        }

        /// <summary>
        /// Patch请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="jsonStr">Json字符串</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> PatchJsonAsync(string url, string jsonStr)
        {
            return SendJsonAsync(HttpMethod.Patch, url, jsonStr);
        }

        #endregion

        #region HttpDelete请求

        /// <summary>
        /// Delete请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <returns>返回 HttpResponseMessage</returns>
        public Task<HttpResponseMessage> DeleteAsync(string url)
        {
            return SendObjectAsync(HttpMethod.Delete, url);
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="jsonStr">请求Json字符串</param>
        /// <param name="contentType"></param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 HttpResponseMessage</returns>
        private Task<HttpResponseMessage> PostJsonAsync(string url, string jsonStr, string contentType = "", Dictionary<string, string> headers = null)
        {
            return SendJsonAsync(HttpMethod.Post, url, jsonStr, contentType, headers);
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="data">请求对象</param>
        /// <param name="headers">请求headers</param>
        /// <returns>返回 HttpResponseMessage</returns>
        private Task<HttpResponseMessage> PostAsync(string url, object data = null, Dictionary<string, string> headers = null)
        {
            return SendObjectAsync(HttpMethod.Post, url, data, contentType: null, headers);
        }

        private Task<HttpResponseMessage> SendJsonAsync(HttpMethod method, string url, string jsonStr = "", string contentType = "", Dictionary<string, string> headers = null)
        {
            return SendObjAsync(method, url, jsonStr, isJsonStr: true, contentType, headers);
        }

        private Task<HttpResponseMessage> SendObjectAsync(HttpMethod method, string url, object data = null, string contentType = "", Dictionary<string, string> headers = null)
        {
            return SendObjAsync(method, url, data, isJsonStr: false, contentType, headers);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="method">请求方法（GET/POST/PUT/PATCH/DELETE）</param>
        /// <param name="url">请求参数</param>
        /// <param name="data">请求数据</param>
        /// <param name="isJsonStr">是否Json字符串(true,false)</param>
        /// <param name="contentType">默认：application/json</param>
        /// <param name="headers">默认：null</param>
        /// <returns></returns>
        private async Task<HttpResponseMessage> SendObjAsync(HttpMethod method, string url, object data = null, bool isJsonStr = false, string contentType = "", Dictionary<string, string> headers = null)
        {
            var baseAddress = SetHttpClientBaseAddress();
            if (!string.IsNullOrEmpty(baseAddress))
            {
                _client.BaseAddress = new Uri(baseAddress);
            }

            #region 构建Content（默认ContentType为 application/json  utf-8）
            var requestDataStr = string.Empty;
            var content = (HttpContent)null;
            if (data != null)
            {
                requestDataStr = isJsonStr ? data.ToString() : data.Serialize();
                content = new StringContent(requestDataStr, Encoding.UTF8, "application/json");
                if (!string.IsNullOrEmpty(contentType))
                {
                    content.Headers.ContentType = new MediaTypeHeaderValue(contentType) { CharSet = "utf-8" };
                }
            }

            if (headers != null && headers.Count > 0)
            {
                foreach (var item in headers)
                {
                    _client.DefaultRequestHeaders.TryAddWithoutValidation(item.Key, HttpUtility.UrlEncode(item.Value));
                }
            }          
            #endregion

            #region 发送请求 (GET/POST/PUT/PATCH/DELETE)

            HttpResponseMessage response = null;
            try
            {
                switch (method.Method)
                {
                    case "POST":
                        response = await _client.PostAsync(url, content);
                        break;
                    case "PUT":
                        response = await _client.PutAsync(url, content);
                        break;
                    case "PATCH":
                        response = await _client.PatchAsync(url, content);
                        break;
                    case "DELETE":
                        response = await _client.DeleteAsync(url);
                        break;
                    case "GET":
                        response = await _client.GetAsync(url);
                        break;
                    default:
                        throw new HttpProxyException($"Http method {method.Method} NotSupported");
                }

                var resStr = await response.Content.ReadAsStringAsync();
              
            }
            catch (Exception ex)
            {
                _logger.LogError("{url}{response}", $"{_client.BaseAddress}{response.RequestMessage.RequestUri.PathAndQuery}",$"网络异常，请稍后重试！{ex.Message}----{ex.StackTrace}");
                throw new HttpProxyException($"网络异常，请稍后重试！");
            }

            #endregion

            return response;
        }

        private async Task<T> ConvertResponseResult<T>(HttpResponseMessage response)
        {
            var resStr = await response.Content.ReadAsStringAsync();
            if (response.IsSuccessStatusCode)
            {
                var jsonSettings = new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                };

                return await Task.FromResult(JsonConvert.DeserializeObject<T>(resStr, jsonSettings));
            }

            throw new HttpProxyException($"服务异常,StatusCode:{response.StatusCode},URL:{response.RequestMessage.RequestUri.PathAndQuery},Response:{resStr},Server:{_client.BaseAddress}");
        }

        public string ConvertRequestToQueryString(object request)
        {
            return string.Join(
                "&",
                request.GetType()
                .GetProperties()
                .Where(p => p.GetValue(request, null) != null)
                .Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(request).ToString())}"));
        }

        protected static string ConvertRequestToCommonFormUrlEncode(object request)
        {
            var requestStr = request.Serialize();
            var encodeRequestStr = WebUtility.UrlEncode(requestStr);
            var dic = new Dictionary<string, string>
            {
                {"params", encodeRequestStr}
            };
            return dic.Select(m => m.Key + "=" + m.Value).Aggregate((m, n) => m + "&" + n);
        }

        protected void SetHeader(string key, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                _client.DefaultRequestHeaders.Add(key, value);
            }
        }

        #endregion

        #region SetAccessToken

        public void SetAccessToken(string scope, string token)
        {
            if (string.IsNullOrEmpty(scope))
            {
                throw new HttpProxyException($"代理接口请求基础服务“{nameof(scope)}”为空");
            }

            if (!string.IsNullOrEmpty(token))
            {
                _client.SetBearerToken(token);
            }
        }

        #endregion

        public static string ToIsoEncoding(string str)
        {
            return Encoding.GetEncoding("ISO-8859-1")
                .GetString(Encoding.Convert(Encoding.Unicode, Encoding.UTF8, Encoding.Unicode.GetBytes(str)));
        }

        private static ContentDispositionHeaderValue GetContentDisposition(string fileName)
        {
            var heardValue = new ContentDispositionHeaderValue("form-data")
            {
                Name = "file",
            };

            heardValue.Parameters.Add(new NameValueHeaderValue("filename", "\"" + ToIsoEncoding(fileName) + "\""));
            return heardValue;
        }
    }
}
